﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using ProgNetComponents.Design.Attributes;

namespace ProgNetComponents.Design.TypeConverters
{
    public class StandardDifferentCtorTypeConverter : DifferentCtorTypeConverter
    {
        protected override System.ComponentModel.Design.Serialization.InstanceDescriptor GetInstanceDescriptor(object value)
        {
            Dictionary<int, List<PropInfo>> ctorList = new Dictionary<int, List<PropInfo>>();
            List<int> skipList = new List<int>();
            List<int> allCtors = new List<int>();
            try
            {
                if (value != null)
                {
                    PropertyInfo[] properties = value.GetType().GetProperties();
                    ConstructorInfo[] ctors = value.GetType().GetConstructors();
                    object[] attributes = null;
                    object val = null;
                    foreach (PropertyInfo pi in properties)
                    {
                        attributes = pi.GetCustomAttributes(typeof(CtorParamAttribute), false);

                        if (attributes.IsNotNull())
                        {
                            val = pi.GetValue(value);

                            foreach (CtorParamAttribute a in attributes)
                            {
                                if (!allCtors.Contains(a.CtorNo))
                                    allCtors.Add(a.CtorNo);

                                if (!skipList.Contains(a.CtorNo))
                                {
                                    if (val != null || a.AllowNull)
                                    {
                                        if (!ctorList.ContainsKey(a.CtorNo))
                                            ctorList.Add(a.CtorNo, new List<PropInfo>());

                                        PropInfo p = new PropInfo();
                                        p.Property = pi;
                                        p.No = a.ParamNo;
                                        p.Value = val;
                                        ctorList[a.CtorNo].Add(p);
                                    }
                                    else
                                    {
                                        skipList.Add(a.CtorNo);
                                        if (ctorList.ContainsKey(a.CtorNo))
                                        {
                                            ctorList.Remove(a.CtorNo);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    allCtors.Sort();
                    Type[] cParams = null;
                    object[] Values = null;
                    ConstructorInfo c = null;
                    foreach (int i in allCtors)
                    {
                        if (ctorList.ContainsKey(i))
                        {
                            List<PropInfo> l = ctorList[i];
                            if (l != null)
                            {
                                cParams = new Type[ctorList[i].Count];
                                Values = new object[ctorList[i].Count];
                                foreach (PropInfo p in l)
                                {
                                    cParams[p.No] = p.Property.PropertyType;
                                    Values[p.No] = p.Value;
                                }
                                c = value.GetType().GetConstructor(cParams);
                                if (c != null)
                                    return new System.ComponentModel.Design.Serialization.InstanceDescriptor(c, Values);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Logger.Default.Log(ex,"StandardDifferentCtorTypeConverter");
            }
            return null;
        }
        private class PropInfo
        {
            public int No
            {
                get;
                set;
            }
            public PropertyInfo Property
            {
                get;
                set;
            }
            public object Value
            {
                get;
                set;
            }
        }
    }
}
